#include <stdio.h>
#include <string.h>

#define fclose_all(a, b, c, d)	\
	do {	\
		fclose((a));	\
		fclose((b));	\
		fclose((c));	\
		fclose((d));	\
	} while (0)

#define IMG_ID_MAGIC		(0xDEADBEEF)
#define IMG_START_MAGIC		(0xFEEDBAC4)

typedef struct image_identifier
{
	unsigned int magic;
	unsigned int image_size;		// size of this structure not included
	unsigned int entry_addr;
	unsigned int sysdata_addr;
}
image_identifier_t;

char *kill_space(char *buf)
{
	while (*buf && (*buf == ' ' || *buf == '\t'))
		buf++;
	return buf;
}
void get_map_info(FILE* map_file, image_identifier_t& imgid)
{
	char buffer[1024];
	while (fgets(buffer, 1024, map_file))
	{
		char *str = (char *)buffer;
		str = kill_space(str);
		
		if (strlen(str) < 2) continue;
		if (str[0] != '0' && str[1] != 'x') continue;
		
		// read the address like (0xC0100000)
		char *numstr = str;
		while (*numstr && *numstr != ' ' && *numstr != '\t')
			numstr++;
		if (numstr - str != 10) continue;
		
		unsigned int addr;
		char number[11] = {0};
		memcpy(number, str, 10);
		sscanf(number, "0x%x", &addr);
		
		str = kill_space(numstr);
		char *funcstr = str;
		while (*funcstr && *funcstr != '\n' && *funcstr != '\r')
			funcstr++;
		*funcstr = '\0';
		
		if (!strcmp(str, "go32"))
		{
			printf("Program entry: 0x%X\n", addr);
			imgid.entry_addr = addr;
		}
		else if (!strcmp(str, "sys_config_data"))
		{
			printf("Global sys config data: 0x%X\n", addr);
			imgid.sysdata_addr = addr;
		}
	}
}

void append_file(FILE *dst, FILE *src)
{
	unsigned char buf[4096];
	fseek(src, 0, SEEK_END);
	unsigned int size = ftell(src);
	rewind(src);
	
	while (size > 0)
	{
		unsigned int actual_sz = (size < 4096) ? size : 4096;
		fread(buf, actual_sz, 1, src);
		fwrite(buf, actual_sz, 1, dst);
		size -= actual_sz;
	}
}

int main(int argc, char *argv[])
{
	if (argc != 5)
	{
		printf("usage: krnlmk ldr krnl mapfile target\n");
		printf("ldr:     the dos extender exe file.\n");
		printf("krnl:    represents the 32-bit krnl image binary.\n");
		printf("mapfile: represents the map file for the krnl image.\n");
		printf("target: the final target file name.\n");
		return 1;
	}
	
	FILE *ldr_file = fopen(argv[1], "rb");
	if (NULL == ldr_file)
	{
		printf("Cannot open ldr file: %s\n", argv[1]);
		return 2;
	}
	
	FILE *krnl_file = fopen(argv[2], "rb");
	if (NULL == krnl_file)
	{
		printf("Cannot open krnl binary file: %s\n", argv[2]);
		fclose(ldr_file);
		return 3;
	}
	
	FILE *map_file = fopen(argv[3], "r");
	if (NULL == map_file)
	{
		printf("Cannot open the map file: %s\n", argv[3]);
		fclose(ldr_file);
		fclose(krnl_file);
		return 4;
	}
	
	FILE *target_file = fopen(argv[4], "wb");
	if (NULL == target_file)
	{
		printf("Cannot create the target file: %s\n", argv[3]);
		fclose(ldr_file);
		fclose(krnl_file);
		fclose(map_file);
		return 5;
	}

	image_identifier_t imgid;
	imgid.magic = IMG_ID_MAGIC;

	// get the size of krnl binary file
	fseek(krnl_file, 0, SEEK_END);
	imgid.image_size = ftell(krnl_file);
	if (!imgid.image_size)
	{
		printf("Invalid krnl binary file size.\n");
		fclose_all(ldr_file, krnl_file, map_file, target_file);
		return 6;
	}
	
	// get other information from map file
	get_map_info(map_file, imgid);
	
	// get and increse the build number
	unsigned int build_version = 0;
	unsigned int build_round = 0;
	FILE *bldver_file = fopen("buildver.h", "r");
	if (bldver_file) fseek(bldver_file, 0, SEEK_END);
	if (bldver_file && ftell(bldver_file))
	{
		rewind(bldver_file);
		fscanf(bldver_file, "#define __VIEWSVER (0x%08X)\n", &build_version);
		fscanf(bldver_file, "#define __VIEWSBLD (%u)\n", &build_round);
		fclose(bldver_file);
	}
	bldver_file = fopen("buildver.h", "w");
	fprintf(bldver_file, "#define __VIEWSVER (0x%08X)\n", build_version);
	fprintf(bldver_file, "#define __VIEWSBLD (%u)\n", build_round + 1);
	fclose(bldver_file);
	
	// prepare the copyright description
	char copyright[64] = {0};
	sprintf(copyright, "viskrnl ver %u.%u.%u.%u (build %u) - James Chou",
		(build_version >> 24), (build_version >> 16) & 0xFF,
		(build_version >> 8) & 0xFF, build_version & 0xFF, build_round);
	
	// writing the final file
	append_file(target_file, ldr_file);
	fwrite(copyright, 64, 1, target_file);
	
	unsigned int krnl_start_magic = IMG_START_MAGIC;
	fwrite(&krnl_start_magic, 4, 1, target_file);
	append_file(target_file, krnl_file);
	fwrite(&imgid, sizeof(imgid), 1, target_file);
	
	fclose_all(ldr_file, krnl_file, map_file, target_file);
	return 0;
}

